home *** CD-ROM | disk | FTP | other *** search
- /************************************
- cDotsDoc.c
-
- SUPERCLASS = CDocument
-
- Methods for the Dots Document. The document has one window. The window
- containts a scrollable dots pane where the matrix is displayed and a
- non-scrolling score pane which displays the score and current player turn.
-
- ************************************/
-
- #include <CApplication.h>
- #include <CBartender.h>
- #include <CDataFile.h>
- #include <CDecorator.h>
- #include <CDesktop.h>
- #include <CError.h>
- #include <Commands.h>
- #include <Constants.h>
- #include <CPanorama.h>
- #include <CPrinter.h>
- #include <CScrollPane.h>
- #include <TBUtilities.h>
-
- #include "cDotsDoc.h"
- #include "cDotsPane.h"
- #include "cScorePane.h"
- #include "dotsTypes.h"
-
- /*** Class Constants ***/
- #define DLOG_PRINT 2000 /* Dialog to display when printing */
- #define kBoxFree 0 /* Initial box state */
- #define kDotsTop 44 /* Top of Dots Pane in window coord */
- #define kScoreBottom (kDotsTop) /* Bottom of Score Pane in window coord */
- #define strABORTPRINT 9 /* String index in STRcommon */
- #define WINDDots 128 /* Resource ID for WIND template */
-
- /*** Globals ***/
- extern CApplication *gApplication;/* The application */
- extern CBartender *gBartender; /* The menu handling object */
- extern CDecorator *gDecorator; /* Window dressing object */
- extern CDesktop *gDesktop; /* The enclosure for all windows */
- extern OSType gSignature; /* The application's signature */
- extern CError *gError; /* The global error handler */
-
- /********** C O N S T R U C T I O N ***********/
-
- /*** IDotsDoc
- *
- * The document's initialization method.
- * If the document has its own instance variables, initialize them here.
- * At least invoke the default method.
- */
- void cDotsDoc::IDotsDoc(CBureaucrat *aSupervisor, Boolean printable)
- {
- int row;
- int col;
-
- CDocument::IDocument(aSupervisor, printable);
-
- fPlayerTurn = kPlayer1; /* player 1 goes first */
-
- /* erase all lines in grid and clear the boxes */
- for (row = 0; row <= kMaxRow; row++)
- {
- for (col = 0; col <= kMaxCol; col++)
- {
- fLines[hLine][row][col] = false;
- fLines[vLine][row][col] = false;
- fBoxes[row][col] = kBoxFree;
- }
- }
-
- }
-
-
- /********** F I L E **********/
-
- /*** NewFile {OVERRIDE}
- *
- * When the user chooses New from the File menu, the CreateDocument()
- * method in the Application class will send a newly created document
- * this message. This method needs to create a new window, ready to
- * work on a new document.
- *
- * Since this method and the OpenFile() method share the code for creating
- * the window, an auxiliary window-building method is used.
- */
- void cDotsDoc::NewFile()
- {
- Str255 wTitle; /* Window title string */
- short wCount; /* Index number of new window */
- Str63 wNumber; /* Index number as a string */
-
- /* Create new window, nothing to display yet so pass null */
- BuildWindow(NULL);
-
- /* Append an index number to the default name of the window */
- itsWindow->GetTitle(wTitle);
- wCount = gDecorator->GetWCount();
- NumToString((long)wCount, wNumber);
- ConcatPStrings(wTitle, (StringPtr) "\p-");
- ConcatPStrings(wTitle, wNumber);
- itsWindow->SetTitle(wTitle);
-
- itsWindow->Select(); /* make it the active window */
- }
-
-
- /*** OpenFile {OVERRIDE}
- *
- * When Open… is chosen from the File menu, the OpenDocument()
- * method in the Application class will let the user choose a file
- * and then send a newly created document this message. The information
- * about the file is in the SFReply record.
- *
- * In this method, open the file and display its contents
- * in a window. This method uses the auxiliary window-building method.
- */
- void cDotsDoc::OpenFile(SFReply *macSFReply)
- {
- CDataFile *theFile;
- Handle theData;
- Str63 theName;
- OSErr theError;
-
- /* Create a file and send it a SFSpecify() message to
- ** set up the name, volume, and directory */
- theFile = new(CDataFile);
- theFile->IDataFile();
- theFile->SFSpecify(macSFReply);
-
- /* Set the instance variable so other methods can use the file if
- ** they need to. If you close the file after reading it, you
- ** should be sure to set itsFile to NULL. */
- itsFile = theFile;
-
- theError = theFile->Open(fsRdWrPerm); /* Send file an Open() message */
-
- /* Check for open error. If error occured, CheckOSError
- ** reports the error in an alert and returns false.
- ** The default error message displays the error number.
- ** Use Estr resources to customize the error message. */
- if (!gError->CheckOSError(theError)) {
- Dispose(); /* Get rid of object since file can't be opened */
- return;
- }
-
- /* Read in the data - don't use rainy day fund, its ok to fail */
- gApplication->RequestMemory(FALSE, TRUE);
- theFile->ReadAll(&theData); /* ReadAll() creates the handle */
- gApplication->RequestMemory(FALSE, FALSE); /* set back to normal */
-
-
- /* If not enough memory to open,
- ** post error (should be -108) and get rid of ourselves. */
- if (theData == NULL) {
- gError->CheckOSError(MemError());
- Dispose();
- return;
- }
-
- /* Use null to build window because dot and
- ** score pane's draw routines do actual display of data */
- BuildWindow(NULL);
-
- storeData(theData); /* Store data as instance variables in document class. */
- DisposHandle(theData);
-
- /* Put file name into window title */
- itsFile->GetName(theName);
- itsWindow->SetTitle(theName);
- itsWindow->Select(); /* Don't forget to make the window active */
- ((CDirector*)itsWindow->itsSupervisor)->Activate();
- /* Select only activates if application was in background!! */
- }
-
-
- /*** BuildWindow
- *
- * This is the auxiliary window-building method that the
- * NewFile() and OpenFile() methods use to create a window.
- *
- * In this implementation, the argument is the data to display.
- */
- void cDotsDoc::BuildWindow (Handle theData)
- {
- CScrollPane *theScrollPane;
- Rect r;
-
- /* Create window */
- itsWindow = new(CWindow);
- itsWindow->IWindow(WINDDots, FALSE, gDesktop, this);
- /* set windows max and min sizes */
- itsWindow->GetInterior(&r);
- SetRect(&r, MIN_WSIZE, MIN_WSIZE, r.right - r.left, r.bottom - r.top);
- itsWindow->SetSizeRect(&r);
-
- /* create a scroll pane and place it in the window */
- theScrollPane = new(CScrollPane);
- theScrollPane->IScrollPane(itsWindow, this, 0, 0, 0, 0,
- sizELASTIC, sizELASTIC, TRUE, TRUE, TRUE);
- theScrollPane->FitToEnclFrame(TRUE, TRUE);
- /* Leave space at top of window for score pane, it does not scroll */
- SetRect(&r, 0, kDotsTop, 0, 0);
- theScrollPane->ChangeSize(&r, FALSE);
-
- /* Create dots pane and place it inside the scroll pane */
- fDotsPane = new(cDotsPane);
- fDotsPane->IDotsPane(theScrollPane, this, 0, 0, 0, 0, sizELASTIC, sizELASTIC);
- /* Fit dots pane to interior of scroll pane.
- ** Interior of scroll pane excludes scroll bars */
- fDotsPane->FitToEnclosure(TRUE, TRUE);
- /* Bounds were initialized to 0's so must set bounds to new frame size */
- fDotsPane->GetFrameSpan(&r.right, &r.bottom);
- SetRect(&r, 0, 0, r.right, r.bottom);
- fDotsPane->SetBounds(&r);
- /* Put dots pane in panorama */
- theScrollPane->InstallPanorama(fDotsPane);
- itsGopher = fDotsPane; /* Dots pane will receive most of the clicks */
-
- /* Create score pane in the window */
- fScorePane = new(cScorePane);
- fScorePane->IScorePane(itsWindow, this, 0, 0, 0, 0, sizELASTIC, sizFIXEDTOP);
- fScorePane->FitToEnclFrame(TRUE, TRUE);
- /* Position above dots pane */
- fScorePane->GetFrame(&r);
- SetRect(&r, 0, 0, 0, kScoreBottom - r.bottom);
- fScorePane->ChangeSize(&r, FALSE);
-
- /* Allow for staggered window placement (NOTE: If this routine is to
- ** be used at all, it must come after subviews are placed in window
- ** or strange things happen) */
- gDecorator->PlaceNewWindow(itsWindow);
- }
-
-
- /*** DoSave {OVERRIDE}
- *
- * This method handles what happens when Save is chosen from the
- * File menu. This method should return TRUE if the file save was successful.
- * If there is no file associated with the document, send a
- * DoSaveFileAs() message.
- */
- Boolean cDotsDoc::DoSave()
- {
- OSErr errCode;
- long theLength;
- long totalLength;
- int x;
-
- if (itsFile == NULL)
- return(DoSaveFileAs());
- else
- {
- /* Go to beginning of file */
- errCode = ((CDataFile *)itsFile)->SetMark(0, fsFromStart);
- if (errCode != noErr)
- return(gError->CheckOSError(errCode));
-
- /* write player turn */
- if (fPlayerTurn == kPlayer1)
- x = 1;
- else
- x = 2;
- errCode = ((CDataFile*)itsFile)->WriteSome((Ptr)&x, 2L);
- if (errCode != noErr)
- return(gError->CheckOSError(errCode));
- totalLength = 2;
-
- /* write line states */
- theLength = sizeof(tLines);
- errCode = ((CDataFile*)itsFile)->WriteSome((Ptr)fLines, theLength);
- if (errCode != noErr)
- return(gError->CheckOSError(errCode));
- totalLength += theLength;
-
- /* write box states */
- theLength = (long)sizeof(tBoxes);
- errCode = ((CDataFile*)itsFile)->WriteSome((Ptr)fBoxes, theLength);
- if (errCode != noErr)
- return(gError->CheckOSError(errCode));
- totalLength += theLength;
-
- /* Set file length in case previous contents was bigger */
- errCode = ((CDataFile *)itsFile)->SetLength(totalLength);
- if (errCode == noErr) /* Force write to disk */
- errCode = FlushVol("", itsFile->volNum);
- if (errCode != noErr)
- return(gError->CheckOSError(errCode));
-
- dirty = FALSE; /* Document is no longer dirty */
- gBartender->DisableCmd(cmdSave);
- return(TRUE); /* Save was successful */
- }
- }
-
-
- /*** DoSaveAs {OVERRIDE}
- *
- * This method handles what happens when Save As… is chosen from
- * File menu. The default DoCommand() method for documents sends a DoSaveFileAs()
- * message which displays a standard put file dialog and sends this message.
- * The SFReply record contains all the information about the file you're about
- * to create.
- */
- Boolean cDotsDoc::DoSaveAs(SFReply *macSFReply)
- {
- /* Close already open file and release its memory */
- if (itsFile != NULL)
- itsFile->Dispose();
-
- /* Display standard file dialog and create new file */
- itsFile = new(CDataFile);
- ((CDataFile *)itsFile)->IDataFile();
- itsFile->SFSpecify(macSFReply);
- itsFile->CreateNew(gSignature, 'DATA');
- itsFile->Open(fsRdWrPerm);
-
- /* Set window title to new file name */
- itsWindow->SetTitle(macSFReply->fName);
-
- return( DoSave() ); /* save normally */
- }
-
-
- /*** DoRevert {OVERRIDE}
- *
- * Close the current file (without writing anything out)
- * and read the last saved version of the file.
- */
- void cDotsDoc::DoRevert()
- {
- Point homePos;
- Handle theData;
-
- /* Make sure file is open. If file is closed after an OpenFile()
- ** then some other method must be used to store file info */
- if (itsFile == NULL)
- return; /* need an error message here */
-
- /* Close file and reopen */
- itsFile->Close();
- if (!gError->CheckOSError(itsFile->Open(fsRdWrPerm)))
- return;
-
- /* &&& Dispose of any current (in memory) contents of the document */
-
- /* Get rid of last undo */
- if (lastTask != NULL) {
- lastTask->Dispose();
- lastTask = NULL;
- }
-
- /* Read in the data */
- gApplication->RequestMemory(FALSE, TRUE); /* Don't tap fund, can fail */
- ((CDataFile *)itsFile)->ReadAll(&theData); /* ReadAll() creates the handle */
- gApplication->RequestMemory(FALSE, FALSE); /* set back to normal */
-
- /* Check to see if data was read */
- if (theData == NULL) {
- gError->CheckOSError(MemError());
- Dispose();
- return;
- }
-
- /* Store the data into the document */
- storeData(theData);
- DisposHandle(theData); /* Don't need this anymore */
-
- /* set panoramas back to home position */
- ((CPanorama*)fDotsPane)->GetHomePosition(&homePos);
- ((CPanorama*)fDotsPane)->ScrollTo(homePos, FALSE);
-
- /* Force redraw of panes on update */
- fDotsPane->Refresh();
- fScorePane->Refresh();
-
- dirty = FALSE;
- }
-
-
- /*** storeData
- *
- * Store the file data as as instance variables in the document
- */
- void cDotsDoc::storeData(Handle theData)
- {
- int x;
- long count;
-
- HLock(theData);
- /* read whose turn it is */
- BlockMove(*theData, &x, 2);
- if (x == 1)
- fPlayerTurn = kPlayer1;
- else
- fPlayerTurn = kPlayer2;
-
- /* read the line states */
- x = sizeof(tLines);
- count = (long)x;
- BlockMove(*theData+2, fLines, count);
-
- /* read the box states */
- count = (long)sizeof(tBoxes);
- BlockMove(*theData+x+2, fBoxes, count);
- HUnlock(theData);
- }
-
-
- /******* P R I N T I N G ********/
-
- /*** PageCount {OVERRIDE}
- *
- * Return the number of pages in a Document
- */
- short cDotsDoc::PageCount()
- {
- long pixWidth;
- long pixHeightDots;
- long pixHeightScore;
-
- /* Get dimensions of dots pane */
- fDotsPane->GetPixelExtent(&pixWidth, &pixHeightDots);
- /* Get dimensions of score pane */
- fScorePane->GetPixelExtent(&pixWidth, &pixHeightScore);
- /* Pages have a fixed pixel height */
- return((pixHeightDots + pixHeightScore) / pageHeight + 1);
- }
-
-
- /*** AboutToPrint {OVERRIDE}
- *
- * The specified range of pages is about to be printed, display print banner
- * and let the panes know they are about to be printed.
- */
- void cDotsDoc::AboutToPrint(
- short *firstPage,
- short *lastPage)
- {
- Str255 docName;
-
- inherited::AboutToPrint(firstPage, lastPage);
-
- /* Let the panes know they're supposed to print */
- fDotsPane->AboutToPrint(firstPage, lastPage);
- fScorePane->AboutToPrint(firstPage, lastPage);
-
- /* Display banner to let user know printing is taking place */
- PositionDialog('DLOG', DLOG_PRINT);
- printBanner = GetNewDialog(DLOG_PRINT, NULL, (WindowPtr) -1L);
- DrawDialog(printBanner);
-
- /* Document name shown in status box when printing to a
- ** LaserWriter is taken from the title of the top window,
- ** which is now our dialog box */
- GetName(docName);
- SetWTitle(printBanner, docName);
- }
-
-
- /*** PrintPageOfDoc {OVERRIDE}
- *
- * Print a single page of the document. This method must NOT send
- * messages to the Printer object. At this time, the print driver
- * is open and waiting for information to be drawn. Requests for
- * status information, such as page specifications, will foul up
- * the state of the print driver.
- */
- void cDotsDoc::PrintPageOfDoc(short pageNum)
- {
- fDotsPane->PrintPage(pageNum, pageWidth, pageHeight);
- fScorePane->PrintPage(pageNum, pageWidth, pageHeight);
- }
-
-
- /*** DonePrinting {OVERRIDE}
- *
- * Print loop has been completed so remove print banner and let panes
- * know they are done printing.
- */
- void cDotsDoc::DonePrinting()
- {
- short itemType;
- Handle item;
- Rect box;
- Str63 abortStr;
- long ticks;
-
- /* If print job aborted display abort message */
- if (PrError() == iPrAbort) {
- GetDItem(printBanner, 1, &itemType, &item, &box);
- GetIndString(abortStr, STRcommon, strABORTPRINT);
- SetIText(item, abortStr);
- Delay(120, &ticks);
- }
-
- DisposDialog(printBanner); /* Get rid of print banner */
-
- /* Let the panes know that printing is finished */
- fDotsPane->DonePrinting();
- fScorePane->DonePrinting();
-
- /* Changing the cursor back to an arrow after printing is
- ** normally accomplished through the main event loop. However,
- ** when printing multiple documents from the finder control does
- ** not return to the main event loop between print jobs and
- ** succeeding print dialogs were using the left over watch cursor.
- ** The following statement was used to correct this. */
- SetCursor(&arrow);
- }
-
-
- /********* G A M E ***********/
-
- /** getBoxState
- *
- * Get state of a box. 3 or less means not yet completed, 4 means completed
- * by player 1, 5 means completed by player 2
- */
- tBoxState cDotsDoc::getBoxState(int row, int col)
- {
- return(fBoxes[row][col]);
- }
-
-
- /*** changeBoxState
- *
- * Change state of box
- */
- Boolean cDotsDoc::changeBoxState(int row, int col,
- Boolean thePlayer, Boolean addingLine)
- {
- tBoxState boxState;
- Boolean changed;
- int delta;
-
- changed = false; /* assume box ownership did not changed */
- boxState = fBoxes[row][col]; /* get old state */
-
- if (addingLine) {
-
- /* Is box completed? */
- if (boxState < kBoxPlayer1) {
- boxState = boxState + 1;
- if (boxState == kBoxPlayer1) {
- changed = true; /* box ownership changed */
-
- /* Did player 2 complete box? */
- if (thePlayer == kPlayer2)
- boxState = kBoxPlayer2;
-
- /* Box completed, so change box and score */
- fDotsPane->invalBox(row, col);
- fScorePane->invalScore(thePlayer);
- }
- }
- } else { /* Remove line */
- /* If box was completed redraw box and score */
- if (boxState >= kBoxPlayer1) {
- changed = true; /* box ownership changed */
- fDotsPane->invalBox(row, col);
- fScorePane->invalScore(thePlayer);
- }
-
- /* Update state of box */
- if (boxState == kBoxPlayer2)
- boxState = 3;
- else
- boxState = boxState - 1;
- }
- fBoxes[row][col] = boxState; /* Change data structure */
- return changed;
- }
-
-
- /*** getLineState
- *
- * Get state of line. True means there is a line in direction from
- * the input dot, false no line
- */
- tLineState cDotsDoc::getLineState(tLineDir direction, int row, int col)
- {
- return(fLines[direction][row][col]);
- }
-
-
- /*** setLineState
- *
- * Change state of line. Set to true for a line or false for no line
- */
- void cDotsDoc::setLineState(tLineDir direction, int row, int col,
- Boolean thePlayer, tLineState newState)
- {
- Boolean aChange, bChange;
- Boolean oldState;
-
- aChange = bChange = false; /* Assume box ownership did not change */
- oldState = getLineState(direction, row, col);
- if (oldState != newState) {
- fLines[direction][row][col] = newState;
- fDotsPane->invalLine(direction, row, col); /* must redraw line */
-
- /* Change state of any box to right or under line */
- if ((row < kMaxRow) && (col < kMaxCol))
- aChange = changeBoxState(row, col, thePlayer, newState);
-
- /* Change state of any box to left or above line */
- if (direction == hLine) {
- if (row > 0)
- bChange = changeBoxState(row-1, col, thePlayer, newState);
- } else {
- if (col > 0)
- bChange = changeBoxState(row, col-1, thePlayer, newState);
- }
-
- /* If box ownership did not change it is other player's turn */
- if (!aChange && !bChange) {
- fPlayerTurn = !fPlayerTurn; /* Let other player have turn */
- fScorePane->setTurn(fPlayerTurn); /* Make score view show whose turn it is */
- }
- }
- }
-
-
- /*** getPlayerTurn
- *
- * Identify player whose move it is
- */
- Boolean cDotsDoc::getPlayerTurn()
- {
- return(fPlayerTurn);
- }